Durante años, WebSockets ha sido la respuesta automática a casi cualquier requisito de tiempo real. Un dashboard con precios en directo, notificaciones, logs, inventario, alertas, estado de pedidos o respuestas de inteligencia artificial que aparecen palabra a palabra suelen llevar a la misma decisión: abrir un WebSocket y mantenerlo vivo. Parece la opción seria, la moderna, la que cualquier equipo técnico elegiría sin demasiada discusión.
Pero esa decisión no siempre es la mejor. Server-Sent Events, o SSE, sigue siendo una alternativa muy útil cuando el flujo de datos va en una sola dirección: del servidor al cliente. Funciona sobre HTTP, está soportado de forma nativa por el navegador, permite reconexión automática con EventSource y evita parte de la complejidad operativa que aparece cuando se despliegan WebSockets a escala.
El debate se ha reactivado porque algunos equipos han medido diferencias relevantes en memoria cuando escalan a decenas o cientos de miles de conexiones. En determinados escenarios, SSE puede consumir menos recursos y simplificar la infraestructura. Pero otros benchmarks muestran lo contrario: WebSockets puede comportarse mejor en memoria y rendimiento, incluso en comunicaciones unidireccionales, dependiendo del framework, el servidor, el tamaño de mensaje, la frecuencia de envío y la implementación concreta.
La conclusión más útil es también la más incómoda: hay que elegir por patrón de comunicación, infraestructura y pruebas propias, no por costumbre. Ni SSE gana siempre ni WebSockets es la opción correcta por defecto.
Dos tecnologías parecidas para problemas distintos
WebSockets crea una conexión persistente bidireccional entre cliente y servidor. Una vez establecida, ambas partes pueden enviar y recibir mensajes en cualquier momento. Es ideal para chats, editores colaborativos, juegos multijugador, sistemas con eventos frecuentes en ambos sentidos, herramientas de trading interactivas o aplicaciones donde el cliente necesita enviar señales constantes al servidor.
SSE mantiene una conexión HTTP abierta en la que el servidor envía eventos al cliente. El cliente escucha. No hay comunicación bidireccional sobre el mismo canal. Si el usuario necesita enviar algo, lo habitual es hacerlo mediante una petición HTTP normal, por ejemplo un POST, y dejar el stream SSE para la respuesta o las actualizaciones.
Ese patrón encaja muy bien con respuestas de modelos de inteligencia artificial, dashboards, feeds, logs de despliegue, barras de progreso, métricas, alertas o notificaciones pasivas. En esos casos el cliente no necesita hablar todo el tiempo. Solo necesita recibir.
Caso de uso | Opción más natural |
Chat con escritura en ambos sentidos | WebSockets |
Editor colaborativo | WebSockets |
Juego multijugador | WebSockets |
Dashboard de métricas | SSE o WebSockets |
Respuesta de IA token a token | SSE |
Logs de despliegue | SSE |
Notificaciones pasivas | SSE |
Datos binarios frecuentes | WebSockets |
Feed de mercado con interacción intensa | Depende de frecuencia y carga |
La diferencia de arquitectura importa porque termina afectando a memoria, balanceadores, proxies, reconexión, autenticación, observabilidad y coste.
SSE gana cuando el servidor habla y el cliente escucha
SSE tiene una virtud muy clara: es simple. En su forma básica basta con devolver una respuesta HTTP con Content-Type: text/event-stream y escribir eventos conforme se producen. El navegador puede recibirlos con EventSource, reconectar si se cae la conexión y gestionar identificadores de evento si el servidor implementa reanudación.
Ese diseño reduce código y elimina decisiones que en WebSockets suelen aparecer pronto: reconexión, backoff, heartbeat, estado de sesión, gestión de canales, compatibilidad con proxies y librerías adicionales. En muchos productos toda esa complejidad se añade antes de saber si realmente hacía falta.
Las respuestas generativas de IA son un ejemplo claro. El usuario envía un prompt mediante una petición normal y el servidor devuelve tokens por streaming. No hace falta mantener un canal bidireccional completo para cada conversación si el patrón principal es «el usuario pide, el servidor genera y el cliente pinta».
SSE también se integra bien con parte de la infraestructura HTTP existente. Headers, cookies, monitorización, trazas, balanceadores y herramientas de observabilidad entienden HTTP. Aun así, «es HTTP» no significa «funciona sin tocar nada». Nginx puede bufferizar eventos, algunos balanceadores no están bien preparados para conexiones largas y ciertos frameworks bloqueantes pueden agotar hilos si cada conexión abierta consume un worker.
Ventaja de SSE | Implicación práctica |
Funciona sobre HTTP | Menos problemas con infraestructura existente |
API nativa EventSource | Implementación muy rápida en navegador |
Reconexión automática | Menos código de cliente |
Diseñado para server-to-client | Perfecto para streaming pasivo |
Modelo sencillo | Menos estado que mantener |
SSE no es una tecnología menor. Es una herramienta que muchos equipos olvidan porque WebSockets se convirtió en sinónimo de «real-time».
WebSockets gana cuando hay ida y vuelta real
WebSockets sigue siendo la opción cuando la aplicación necesita comunicación frecuente en ambos sentidos. Un chat con indicadores de escritura, un editor colaborativo, una sesión de juego, una herramienta de trading interactiva o un sistema de control remoto no encajan bien en SSE. Forzar SSE en esos casos suele terminar en una mezcla de stream para recibir y peticiones paralelas para enviar, con más complejidad de la necesaria.
También puede tener ventaja en rendimiento cuando se usan implementaciones muy ajustadas. WebSockets trabaja con framing propio y puede manejar mejor datos binarios. En cargas con mensajes pequeños y frecuentes, algunos tests muestran menor uso de memoria frente a SSE. Si además se usan servidores especializados como uWebSockets.js o implementaciones muy afinadas en Go, Rust o C++, el techo de conexiones concurrentes puede ser bastante superior.
El problema aparece cuando se elige WebSockets para un caso que no lo necesita. Entonces el equipo asume estado persistente, reconexión manual, backpressure, heartbeat, escalado horizontal y configuración de infraestructura sin obtener una ventaja clara. Es como instalar una autopista para mover una cinta transportadora de un solo sentido.
El rendimiento depende demasiado del contexto
Los titulares del tipo «SSE usa un 40 % menos de memoria» o «WebSockets siempre rinde mejor» hay que leerlos con cuidado. Ambos pueden ser ciertos en escenarios distintos.
En una prueba con 100.000 conexiones donde el servidor solo empuja actualizaciones poco frecuentes, SSE puede resultar más eficiente si la implementación HTTP es ligera y el framework maneja bien conexiones largas. En otro entorno, con Socket.IO, NestJS, Kubernetes, HTTP/2 y pruebas controladas, WebSockets puede consumir menos memoria y comportarse mejor, incluso cuando se usa solo para envío del servidor al cliente.
La diferencia no está solo en el protocolo. Está en todo lo que lo rodea: runtime, librería, proxy, kernel, TLS, HTTP/1.1 o HTTP/2, tamaño de mensaje, frecuencia, serialización, gestión de buffers, heartbeats, reconexión, GC, límites de contenedor y observabilidad.
Variable | Por qué cambia el resultado |
Frecuencia de mensajes | No es igual un evento por segundo que cientos por minuto |
Tamaño del payload | 100 bytes y 50 KB ejercen presiones distintas |
Framework | Node, Go, Java, Python o PHP manejan conexiones de forma diferente |
Librería | Socket.IO no equivale a WebSocket puro |
Proxy | Nginx o balanceadores pueden bufferizar o cortar conexiones |
HTTP/2 | Multiplexa streams, pero puede introducir comportamientos específicos |
Autenticación | Cookies, headers y tokens cambian la implementación |
Reconexión | Puede generar tormentas de conexiones si no se controla bien |
Por eso lo más sensato es medir con la carga esperada. No con una demo. No con una tabla genérica. Con el patrón real de la aplicación.
El problema de EventSource: cómodo, pero limitado
La API nativa EventSource es muy cómoda, pero tiene limitaciones conocidas. No permite enviar headers personalizados, no soporta POST, ofrece un manejo de errores bastante limitado y obliga a trabajar con GET. Para muchas aplicaciones internas puede ser suficiente, sobre todo si se usan cookies HttpOnly para autenticación. Para APIs con bearer tokens, clientes no navegador o flujos más complejos, se queda corta.
Una alternativa moderna es usar fetch con streams y parsear el formato text/event-stream con una librería como eventsource-parser. Así se recupera control sobre headers, método, errores y credenciales. A cambio, el equipo debe encargarse de la reconexión, el backoff, la limpieza, el control de Last-Event-ID y la gestión de fallos.
Enfoque | Ventaja | Coste |
EventSource nativo | Muy simple y reconecta solo | Sin headers personalizados ni POST |
SSE con fetch streams | Control total sobre request y errores | Hay que gestionar la reconexión |
WebSocket puro | Bidireccional y flexible | Más gestión de estado |
Socket.IO | Reconexión y abstracción incluidas | Más dependencia y overhead |
En producción, la simplicidad inicial de SSE suele completarse con detalles que los tutoriales no explican: heartbeats, timeouts, desactivación del buffering, límites de conexión y estrategia de reanudación.
Reglas prácticas para elegir
Lo primero es mirar la dirección del flujo. Si el servidor emite y el cliente escucha, SSE merece estar en la conversación. Si cliente y servidor hablan constantemente, WebSockets será más natural.
Después, la frecuencia. Para eventos ocasionales, dashboards, notificaciones y streaming de texto, SSE suele ser suficiente. Para interacciones muy frecuentes y baja latencia en ambos sentidos, WebSockets tiene más sentido.
La infraestructura también importa. En Nginx hay que desactivar el buffering para que los eventos SSE no se acumulen y lleguen tarde, y conviene configurar timeouts y mensajes de heartbeat. En entornos serverless o con balanceadores gestionados, las conexiones largas pueden tener límites que cambian la decisión.
Y no hay que ignorar el framework. Un servidor que asigna un hilo por conexión puede sufrir tanto con SSE como con WebSockets. Node.js, Go o servidores async bien configurados suelen manejar mejor este patrón, pero si alguna parte del handler hace I/O síncrono, el resultado puede degradarse igual.
Menos dogma, más pruebas
El error habitual no es elegir WebSockets. WebSockets es una gran tecnología. El error es elegirlo antes de entender el problema. Sería igual de equivocado elegir SSE por moda inversa, pensando que siempre será más barato o más ligero.
Para un producto de IA con streaming de tokens, SSE encaja muy bien. Para un dashboard con actualizaciones pasivas cada segundo, también. Para una app colaborativa, no. Para una plataforma con 100.000 conexiones, el debate no puede resolverse con teoría: hay que simular usuarios, payloads, frecuencia, reconexiones, picos y fallos de red.
La arquitectura correcta no es la más popular. Es la que hace el trabajo con menos complejidad y coste aceptable. A veces será Content-Type: text/event-stream y un res.write(). Otras, WebSockets con un servidor ajustado, backpressure bien resuelto y una capa de escalado diseñada desde el principio.
El tiempo real no es una tecnología. Es un requisito. Y como cualquier requisito serio, exige medir antes de casarse con una solución.
Preguntas frecuentes
¿SSE puede sustituir a WebSockets?
Solo en casos unidireccionales donde el servidor envía datos y el cliente escucha. Para comunicación bidireccional frecuente, WebSockets sigue siendo la opción natural.
¿SSE consume menos memoria que WebSockets?
Depende de la implementación, la carga, el framework y el patrón de mensajes. Algunos equipos han visto menor consumo con SSE a gran escala, mientras que otros benchmarks muestran mejor comportamiento de WebSockets.
¿Por qué se usa SSE en respuestas de IA en streaming?
Porque el usuario envía una petición y el servidor devuelve tokens progresivamente. Ese patrón es principalmente server-to-client, así que SSE encaja bien.
¿Qué hay que configurar para usar SSE en producción?
Conviene desactivar el buffering en proxies como Nginx, ajustar timeouts, añadir heartbeats, revisar la autenticación, controlar la reconexión y asegurarse de que el framework soporta conexiones largas sin bloquear recursos.
Fuente: administraciondesistemas.com
